package com.geekhalo.schedule.v2;

import com.geekhalo.schedule.task.TaskConfig;
import com.geekhalo.schedule.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class LoadAndRunScheduleService {
    @Autowired
    private TaskService taskService;

    @Autowired
    private TaskDefinitionV2Repository definitionV2Repository;

    @Autowired
    private RedissonClient redissonClient;

    @Scheduled(fixedDelay = 1000, initialDelay = 1000)
    public void loadAndRunTask(){
        Date now = new Date();
        // 加载需要运行的任务：
        // 1. 状态为 ENABLE
        // 2. 下一次运行时间 小于 当前时间
        List<TaskDefinitionV2> shouldRunTasks = loadShouldRunTasks(now);

        // 依次遍历待运行任务，执行对于的任务
        for (TaskDefinitionV2 task : shouldRunTasks){
            // Double Check
            if (task.shouldRun(now)){
                // 执行任务
                runTask(task);

                // 更新任务的下一次运行时间
                updateNextRunTime(task, now);
            }
        }
    }

    @Scheduled(fixedDelay = 1000, initialDelay = 1000)
    public void loadAndRunTaskWithLock(){
        Date now = new Date();
        // 加载需要运行的任务：
        // 1. 状态为 ENABLE
        // 2. 下一次运行时间 小于 当前时间
        List<TaskDefinitionV2> shouldRunTasks = loadShouldRunTasks(now);
        // 依次遍历待运行任务，执行对于的任务
        for (TaskDefinitionV2 task : shouldRunTasks){
            // Double Check
            if (task.shouldRun(now)){

                // 获取分布式锁，用于保证每个任务只能有一个正在运行
                RLock lock = this.redissonClient.getFairLock("LoadAndRunScheduleService-" + task.getId());
                if (lock.tryLock()) {
                    // 成功获取锁，运行任务
                    try {
                        log.info("Success to get Lock, begin to run task {}", task.getId());

                        // 执行任务
                        runTask(task);

                        // 更新任务的下一次运行时间
                        updateNextRunTime(task, now);
                        log.info("Success to run task {}", task.getId());
                    }finally {
                        // 任务运行解释，释放锁
                        lock.unlock();
                    }
                }else {
                    // 未获取锁，打印日志，不做额外处理
                    log.info("Failed to get Lock!!!!");
                }
            }
        }
    }

    private List<TaskDefinitionV2> loadShouldRunTasks(Date now) {
        return this.definitionV2Repository.getByStatusAndNextRunAtLessThan(TaskStatus.ENABLE, now);
    }

    private void updateNextRunTime(TaskDefinitionV2 task, Date now) {
        task.updateNextRunTime(now);
        this.definitionV2Repository.save(task);
    }

    private void runTask(TaskDefinitionV2 task) {
        TaskConfig taskConfig = TaskConfig.builder()
                .name(task.getName())
                .data(task.getData())
                .build();
        this.taskService.runTask(taskConfig);
    }
}
